#[macro_use]
extern crate log;

use std::time::Duration;
use std::time::Instant;

pub mod adapter;
mod usart;

use adapter::ATAdapter;
pub use adapter::ATAdapterRecvError;
pub use adapter::ATAdapterSendError;
pub use usart::Usart;

#[derive(Debug)]
pub struct ATPort<A: ATAdapter> {
    adapter: A,
    urc: crossbeam_queue::ArrayQueue<String>,
}

impl<A: ATAdapter> ATPort<A> {
    pub fn new(adapter: A, cap: usize) -> Self {
        ATPort {
            adapter,
            urc: crossbeam_queue::ArrayQueue::new(cap),
        }
    }

    pub fn urc_pop(&mut self) -> Option<String> {
        if let Some(s) = self.urc.pop() {
            return Some(s);
        }
        if let Ok(s) = self.adapter.try_recv() {
            return Some(s);
        }
        None
    }

    pub fn urc_push(&mut self, s: String) -> Option<String> {
        if self.urc.is_full() {
            warn!("urc drop: {:?}", self.urc.pop())
        }
        self.urc.force_push(s)
    }

    pub fn send(&mut self, cmd: &str) -> Result<usize, ATAdapterSendError> {
        self.adapter.send(cmd)
    }

    pub fn recv_timeout(&mut self, timeout: Duration) -> Result<String, ATAdapterRecvError> {
        self.adapter.recv_timeout(timeout)
    }

    pub fn recv_timeout_equal(
        &mut self,
        res: &str,
        timeout: Duration,
    ) -> Result<String, ATAdapterRecvError> {
        match Instant::now().checked_add(timeout) {
            Some(deadline) => loop {
                match self.adapter.recv_timeout(deadline - Instant::now()) {
                    Ok(s) => {
                        if s == res {
                            return Ok(s);
                        }
                        self.urc_push(s);
                    }
                    Err(_) => {
                        if Instant::now() > deadline {
                            return Err(ATAdapterRecvError::RecvTimeoutError);
                        }
                    }
                }
            },
            None => Err(ATAdapterRecvError::RecvError),
        }
    }

    pub fn recv_timeout_starts_with(
        &mut self,
        pat: &str,
        timeout: Duration,
    ) -> Result<String, ATAdapterRecvError> {
        match Instant::now().checked_add(timeout) {
            Some(deadline) => loop {
                match self.adapter.recv_timeout(deadline - Instant::now()) {
                    Ok(s) => {
                        if s.starts_with(pat) {
                            return Ok(s);
                        }
                        self.urc_push(s);
                    }
                    Err(_) => {
                        if Instant::now() > deadline {
                            return Err(ATAdapterRecvError::RecvTimeoutError);
                        }
                    }
                }
            },
            None => Err(ATAdapterRecvError::RecvError),
        }
    }

    pub fn recv_timeout_strip_prefix(
        &mut self,
        prefix: &str,
        timeout: Duration,
    ) -> Result<String, ATAdapterRecvError> {
        match Instant::now().checked_add(timeout) {
            Some(deadline) => loop {
                match self.adapter.recv_timeout(deadline - Instant::now()) {
                    Ok(s) => {
                        if let Some(r) = s.strip_prefix(prefix) {
                            return Ok(r.to_string());
                        }
                        self.urc_push(s);
                    }
                    Err(_) => {
                        if Instant::now() > deadline {
                            return Err(ATAdapterRecvError::RecvTimeoutError);
                        }
                    }
                }
            },
            None => Err(ATAdapterRecvError::RecvError),
        }
    }
}

#[cfg(test)]
mod test {
    #[test]
    fn test1() {}
}
